package it.polimi.swimv2.session;

import java.util.Collection;

import it.polimi.swimv2.entities.Friendship;
import it.polimi.swimv2.entities.FriendshipRequest;
import it.polimi.swimv2.entities.RequestStatus;
import it.polimi.swimv2.entities.User;
import it.polimi.swimv2.exception.FriendshipRequestException;
import it.polimi.swimv2.remote.FriendshipRequestManagerRemote;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 * Session Bean implementation class FriendshipRequestManager
 */
@Stateless
public class FriendshipRequestManager implements FriendshipRequestManagerRemote, FriendshipRequestManagerLocal {

	@PersistenceContext(unitName = "swimv2")
	EntityManager em;
	
	@EJB FriendsManagerLocal friendsManager;
	/**
	 * Default constructor.
	 */
	public FriendshipRequestManager() {

	}

	@SuppressWarnings("unchecked")
	@Override
	public Collection<FriendshipRequest> receivedFriendshipRequest(
			String username) throws FriendshipRequestException {

		User ureceiver = null;
		if (username != null) {
			ureceiver = em.find(User.class, username);
		}
		if (ureceiver == null) {
			throw new FriendshipRequestException();
		}

		Query q = em.createQuery("SELECT object(fr) "
				+ "FROM FriendshipRequest fr "
				+ "WHERE  fr.receiver =:ureceiver " +
				"AND fr.visibleToReceiver = true");
		q.setParameter("ureceiver", ureceiver);
		Collection<FriendshipRequest> result = q.getResultList();
		updateOldReceivedRequest(result);
		removeOldRequest(ureceiver);
		return result;
	}

	private void updateOldReceivedRequest(Collection<FriendshipRequest> result){
		for(FriendshipRequest fr: result){
			if(fr.getStatus().equals(RequestStatus.Pending)==false){
				fr.setVisibleToReceiver(false);
			}
		}
	}
	private void removeOldRequest(User u) {
		Query q = em.createQuery("DELETE "
				+ "FROM FriendshipRequest fr "
				+ "WHERE  (fr.receiver =:u OR fr.sender=:u) " 
				+ "AND fr.visibleToReceiver = false " 
				+ "AND fr.visibleToSender = false");
		q.setParameter("u", u);
		q.executeUpdate();
	}

	@Override
	public Collection<FriendshipRequest> sentFriendshipRequest(String username)
			throws FriendshipRequestException {

		User usender = null;
		if (username != null) {
			usender = em.find(User.class, username);
		}
		if (usender == null) {
			throw new FriendshipRequestException();
		}

		Query q = em.createQuery("SELECT object(fr) "
				+ "FROM FriendshipRequest fr " 
				+ "WHERE  fr.sender=:usender " 
				+ "AND fr.visibleToSender = true");
		q.setParameter("usender", usender);
		@SuppressWarnings("unchecked")
		Collection<FriendshipRequest> result = q.getResultList();
		updateOldSentRequest(result);
		removeOldRequest(usender);
		return result;
	}
	
	private void updateOldSentRequest(Collection<FriendshipRequest> result){
		for(FriendshipRequest fr: result){
			if(fr.getStatus().equals(RequestStatus.Pending)==false){
				fr.setVisibleToSender(false);
			}
		}
	}

	@Override
	public boolean acceptFriendship(String username, Integer friendshipId)
			throws FriendshipRequestException {
		
		checkNotNull(username, friendshipId);
		User ureceiver = em.find(User.class, username);
		FriendshipRequest fr = em.find(FriendshipRequest.class, friendshipId);
		if (ureceiver != null && fr != null && responseIsAllowed(ureceiver, fr)) {
			fr.accept();
			fr.setVisibleToReceiver(false);
			createFriendship(ureceiver,fr.getSender());
			createFriendship(fr.getSender(),ureceiver);
			em.flush();
			return true;
		} else {
			throw new FriendshipRequestException();
		}
	}
	
	private void createFriendship(User u1,User u2){
		Friendship fr = new Friendship(u1, u2);
		em.persist(fr);
	}
	@Override
	public boolean refuseFriendship(String username, Integer friendshipId)
			throws FriendshipRequestException {
		checkNotNull(username, friendshipId);
		User ureceiver = em.find(User.class, username);
		FriendshipRequest fr = em.find(FriendshipRequest.class, friendshipId);
		if (ureceiver != null && fr != null && responseIsAllowed(ureceiver, fr)) {
			fr.refuse();
			fr.setVisibleToReceiver(false);
			em.flush();
			return true;
		}
		throw new FriendshipRequestException();
	}

	private boolean responseIsAllowed(User ureceiver, FriendshipRequest fr) {
		return ureceiver.equals(fr.getReceiver())
				&& fr.getStatus().equals(RequestStatus.Pending);
	}

	@Override
	public boolean deleteFriendship(String username, Integer friendshipId)
			throws FriendshipRequestException {
		
		checkNotNull(username, friendshipId);
		User usender = em.find(User.class, username);
		FriendshipRequest fr = em.find(FriendshipRequest.class, friendshipId);
		if (usender != null && fr != null && deleteIsAllowed(usender, fr)) {
			fr.delete();
			fr.setVisibleToSender(false);
			em.flush();
			return true;
		}
		throw new FriendshipRequestException();
	}

	private boolean deleteIsAllowed(User usender, FriendshipRequest fr) {
		
		return usender.equals(fr.getSender())
				&& fr.getStatus().equals(RequestStatus.Pending);
	}

	private void checkNotNull(String username, Integer friendshipId)
			throws FriendshipRequestException {
		
		if (username == null || friendshipId == null) {
			throw new FriendshipRequestException();
		}
	}

	@Override
	public boolean sendFriendshipRequest(String sender, String receiver)
			throws FriendshipRequestException {
		
		User usender = em.find(User.class, sender);
		User ureceiver = em.find(User.class, receiver);
		if (areValid(usender, ureceiver) && !alreadyFriends(usender, ureceiver)
				&& !existFriendshipRequest(usender, ureceiver)) {
			FriendshipRequest fr = new FriendshipRequest(usender, ureceiver);
			em.persist(fr);
			return true;
		}
		throw new FriendshipRequestException();
	}

	private boolean areValid(User usender, User ureceiver) {
		
		return usender != null && ureceiver != null
				&& !(usender.getUsername().equals(ureceiver.getUsername()));
	}

	private boolean alreadyFriends(User usender, User ureceiver) {
		
		return friendsManager.areFriends(usender, ureceiver);
	}

	@Override
	public boolean existFriendshipRequest(User u1, User u2) {
		if( u1==null || u2 ==null){
			return false;
		}
		Query q = em.createQuery("SELECT object(fr) "
				+ "FROM FriendshipRequest fr " 
				+ "WHERE fr.status= :status "
				+ "AND((fr.sender=:us AND fr.receiver=:ur) "
				+ "OR(fr.sender=:ur AND fr.receiver=:us))");
		q.setParameter("status", RequestStatus.Pending);
		q.setParameter("us", u1);
		q.setParameter("ur", u2);

		if (q.getResultList().isEmpty()) {
			return false;
		} else {
			return true;
		}
	}

}
